#include "PictureFileStream.h"
#include "mediaPriv.h"
#include "VideoDecode.h"

#include "jpeglib.h"
#include "turbojpeg.h"
#include "jerror.h"
#include "libyuv.h"
#include <string.h>
#include "StreamerCurtainPlay.h"



#if defined(MODEL_ROOMS) || defined(MODEL_AIR)
int is_yuv420(const char *filename) {
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    FILE *infile;
    int yuv420 = 0;  // 默认不是YUV420

    if ((infile = fopen(filename, "rb")) == NULL) {
        fprintf(stderr, "can't open %s\n", filename);
        return -1;
    }

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo, infile);
    if(jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK){
        goto cleanup;
    }

    // 判断是否是YUV420
    if (cinfo.num_components == 3 &&
        cinfo.comp_info[0].h_samp_factor == 2 && cinfo.comp_info[0].v_samp_factor == 2 &&
        cinfo.comp_info[1].h_samp_factor == 1 && cinfo.comp_info[1].v_samp_factor == 1 &&
        cinfo.comp_info[2].h_samp_factor == 1 && cinfo.comp_info[2].v_samp_factor == 1) {
        yuv420 = 1;  // 是YUV420
    }

cleanup:
    // 结束解压缩
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);

    return yuv420;
}


// JpgToI420.cpp: 定义应用程序的入口点。
//
int decompress_jpeg(const char *input_filename, unsigned char **rgb_data, int *width, int *height)
{
    // Step 1: Read the JPEG file
    FILE* jpegFile = fopen(input_filename, "rb");
    if (!jpegFile) {
        printf("Error: Failed to open JPEG file.\n");
        return -1;
    }

    // Obtain file size
    fseek(jpegFile, 0, SEEK_END);
    long jpegFileSize = ftell(jpegFile);
    rewind(jpegFile);

    // Allocate memory to hold the JPEG image
    unsigned char* jpegBuffer = (unsigned char*)malloc(jpegFileSize);
    fread(jpegBuffer, 1, jpegFileSize, jpegFile);
    fclose(jpegFile);

    // Step 2: Decode the JPEG image to RGB
    tjhandle jpegDecompressor = tjInitDecompress();
    if (jpegDecompressor == nullptr) {
        // Handle initialization error
        fprintf(stderr, "Error: Failed to initialize TurboJPEG decompressor.\n");
        free(jpegBuffer);
        return -1;
    }
    int jpegSubsamp;
    int ret = tjDecompressHeader2(jpegDecompressor, jpegBuffer, jpegFileSize, width, height, &jpegSubsamp);
    if (ret != 0) {
        // Handle header decompression error
        fprintf(stderr, "Error: Failed to decompress JPEG header. %s\n", tjGetErrorStr2(jpegDecompressor));
        tjDestroy(jpegDecompressor);  // Don't forget to destroy the decompressor on error
        free(jpegBuffer);
        return ret;
    }

    *rgb_data = (unsigned char*)malloc((*width) * (*height) * 3);  // 3 bytes per pixel for RGB
    if (*rgb_data == nullptr) {
        fprintf(stderr, "Error: Failed to allocate memory for RGB data.\n");
        tjDestroy(jpegDecompressor);
        free(jpegBuffer);
        return -1;
    }
    
    ret = tjDecompress2(jpegDecompressor, jpegBuffer, jpegFileSize, *rgb_data, *width, 0, *height, TJPF_RGB, TJFLAG_FASTDCT);
    if (ret != 0) {
        printf("Error: Failed to decompress jpeg.\n");
        tjDestroy(jpegDecompressor);
        free(jpegBuffer);
        return ret;
    }

    free(jpegBuffer);
    tjDestroy(jpegDecompressor);
    return 0;
}

int CompressYUVToJpg(const char *out_filename, unsigned char *yuv_data, int width, int height)
{
    tjhandle jpegCompressor = tjInitCompress();
    unsigned char* jpegOutBuffer = nullptr; // Corrected type
    unsigned long jpegOutSize = 0;

    int ret = tjCompressFromYUV(jpegCompressor, yuv_data, width, 1, height, TJSAMP_420, &jpegOutBuffer, &jpegOutSize, 75, 0);
    if (ret != 0) {
        printf("Error: Failed to decompress jpeg.\n");
        tjDestroy(jpegCompressor);
        free(jpegOutBuffer);
        return ret;
    }
    // Save the resulting JPEG image
    FILE* outputJpegFile = fopen(out_filename, "wb");
    if (!outputJpegFile) {
        printf("Error: Failed to open JPEG file.\n");
        return -1;
    }
    fwrite(jpegOutBuffer, 1, jpegOutSize, outputJpegFile);
    fclose(outputJpegFile);

    // Free allocated memory
    tjDestroy(jpegCompressor);
    free(jpegOutBuffer);
    return 0;
}

int ConvertPixelToYUVJ420P(const char *input_filename, const char *output_filename)
{
    unsigned char *rgb_data = nullptr;
    int width, height;
    int ret = decompress_jpeg(input_filename, &rgb_data, &width, &height);
    if (ret != 0) {
        printf("Error: Failed to decompress jpeg.\n");
        if (rgb_data != nullptr) free(rgb_data);
        return ret;
    }

    unsigned char *yuv_data = (unsigned char *)malloc(width * height * 3 / 2); // YUVJ420P has 1.5 channels
    if (yuv_data == nullptr) {
        fprintf(stderr, "Error: Failed to allocate memory for YUV data.\n");
        free(rgb_data);
        return -1;
    }
    libyuv::RAWToI420(rgb_data, width * 3, yuv_data, width, yuv_data + width * height, width / 2, yuv_data + width * height * 5 / 4, width / 2, width, height);
    free(rgb_data);
    ret = CompressYUVToJpg(output_filename, yuv_data, width, height);
    if (ret != 0) {
        printf("Error: Failed to compress jpeg.\n");
        if (yuv_data != nullptr) free(yuv_data);
        return ret;
    }

    free(yuv_data);
    return 0;
}
#endif


namespace Media{

PictureFileStream*     PictureFileStream::sm_streamIns;
std::mutex 		       PictureFileStream::sm_insMutex;

PictureFileStream *PictureFileStream::instance()
{
	if(sm_streamIns == NULL)
    {
        std::lock_guard<std::mutex> guard(sm_insMutex);
        if(sm_streamIns == NULL)
        {
            sm_streamIns = new PictureFileStream();
        }
    }
    return sm_streamIns;
}

PictureFileStream::PictureFileStream() 
{ 
    m_taskTimer.reset();
}

bool PictureFileStream::start(StreamPlayInfo playInfo,int curtainNum)
{
    LOG_PrintWarn("start enter \n");
    if (!strstr(playInfo.stream.content, ".jpg"))
    {
        LOG_PrintError("playInfo.stream.content is null \n");
        return false;
    }

    if(!m_started)
    {  
        if(m_decodeChannel != playInfo.videoChn)
        {            
            m_decodeChannel = playInfo.videoChn;
            m_pVideoDecode = VideoDecode::instance(m_decodeChannel); 
        }
        VideoDecFormat decFormat;
        memset(&decFormat, 0, sizeof(VideoDecFormat));
        decFormat.type = videoEncMotionJPEG;
        if((!m_pVideoDecode) || (!m_pVideoDecode->setFormat(&decFormat)))
        {
            return false;
        }
        
        if((!m_pVideoDecode) || (!m_pVideoDecode->start())) //启动解码
        {
            return false;
        }

        if(curtainNum == CustomNum)
        {
            m_fileStreamName = startupPath + playInfo.stream.content;
        }
        else if(curtainNum == StreamerNum)
		{
            std::string path;
            DiskController::instance()->getRealTimeBusinessPath(STREAM_ROOT_DIR, path);
			m_fileStreamName = path + playInfo.stream.content;
            #if defined(MODEL_ROOMS) || defined(MODEL_AIR)
                if(is_yuv420(m_fileStreamName.c_str()) == 0){
                    ConvertPixelToYUVJ420P(m_fileStreamName.c_str(), m_fileStreamName.c_str());
                }
            #endif
		}
		else
		{
            m_fileStreamName = picturePath + playInfo.stream.content;
        }

        m_started = true;
        m_taskInfo = playInfo.task;        
        m_taskTimer.reset(new Fdt::Timer(m_taskInfo.StartTime * 1000, ([this](){return this->timerTaskCb();}), nullptr));

        
    }

    LOG_PrintWarn("start leave \n");

    return true;
}

bool PictureFileStream::timerTaskCb()
{
    m_thread.start([this](Fdt::Thread *thread){this->ThreadProc(thread);}); //启动单独线程处理流
    
    if(m_taskInfo.StopTime != -1)
    {
        m_taskTimer.reset(new Fdt::Timer(m_taskInfo.StopTime * 1000, ([this](){return this->stop();}), nullptr)); //不是循环播放，注册停止函数
    }
    else
    {
        m_taskTimer.reset();
    }

    return true;
}

bool PictureFileStream::stop()
{
    LOG_PrintWarn("stop enter \n");

    if(m_started)
    {
        m_started = false;
        m_taskTimer.reset();
        
        if((!m_pVideoDecode) || (!m_pVideoDecode->stop())) //停止解码
        {
            return false;
        }        
    }

    LOG_PrintWarn("stop leave \n");
    return true;
}

bool PictureFileStream::setFormat(VideoDecFormat *format)
{
    if(!format || !m_pVideoDecode)
	{
	    errorf("format or m_videoDecoder is NULL \n");
		return false;
	}
		
    return m_pVideoDecode->setFormat(format);
}


bool PictureFileStream::setDecPacket()
{
    if(m_pVideoDecode)
    {
        return m_pVideoDecode->putPacket(&m_decodePacket);
    }
    return false;
}

void PictureFileStream::ThreadProc(Fdt::Thread *thread)
{
    thread->setPriority(Fdt::PRIORITY_NORMAL, Fdt::POLICY_RR);


    std::lock_guard<std::mutex> guard(m_fileMutex);
    
	FILE *infile = fopen(m_fileStreamName.c_str(), "rb");
    if (!infile) 
    {
        fprintf(stderr, "Could not open %s\n", m_fileStreamName.c_str());
        return ;
    }

    fseek(infile, 0, SEEK_END);
    int fileSize = ftell(infile); //获取文件大小
    fseek(infile, 0, SEEK_SET);

    printf("fileSize =%d\n",fileSize);
	//大小限制1.25MiB
	//if(fileSize > 1310720){
		//LOG_PrintError("file is too huge\n");
		//return ;
	//}
    
    m_decodePacket.buffer = (uchar *)malloc(fileSize);

    int readSize = fread(m_decodePacket.buffer, 1, fileSize, infile);

    m_decodePacket.length = readSize;

    while(m_started)
    {
        setDecPacket(); //发送给解码器
        usleep(33*1000);
    }

    free(m_decodePacket.buffer);

    fclose(infile);

    return;
}


}


